package com.topc.search.service.impl;



import com.topc.search.domain.User;
import com.topc.search.repository.UserRepository;
import com.topc.search.service.UserService;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.GeoDistanceQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    private List<User> nativeSearchToList(NativeSearchQueryBuilder nativeSearchQueryBuilder){
        SearchHits<User> searchHits = elasticsearchRestTemplate.search
                (nativeSearchQueryBuilder.build(), User.class, IndexCoordinates.of("hotel-user-test"));
        return searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    @Override
    public void createList(List<User> userList) {
        userRepository.saveAll(userList);
    }

    @Override
    public void create(User user) {
       userRepository.save(user);
    }

    @Override
    public void update(User user) {
         userRepository.save(user);
    }
    /**
     * es语句
     *
     *GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "match_all": {}
     *   }
     * }
     * */
    @Override
    public List<User> searchAll() {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        nativeSearchQueryBuilder.withQuery(matchAllQueryBuilder);
        return nativeSearchToList(nativeSearchQueryBuilder);
    }

    @Override
    public void delete(Long id) {
        userRepository.deleteById(id);
    }

    /**
     *范围查询
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "range": {
     *       "property": {
     *         "gte": 100,
     *         "lte": 15000.5
     *       }
     *     }
     *   }
     * }
     *
     * gt 大于  gte大于等于  lt小于  lte小于等于
     * */
    @Override
    public List<User> searchRange(String keyFiled,double gte, double lte) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.rangeQuery(keyFiled).gte(gte).lte(lte));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }


    /**
     * 关键词查询
     * GET /hotel-user-test/_search
     * {
     *  "query": {
     *    "term": {
     *      "sex": {
     *        "value": 1
     *      }
     *    }
     *  }
     * }
     *
     * */
    @Override
    public List<User> searchTerm(String keyField, Object keyWord) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.termQuery(keyField, keyWord));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }

    /**
     *
     * GET /hotel-user-test/_search
     * {
     *  "query": {
     *    "prefix": {
     *      "detail": {
     *        "value": "abc"
     *      }
     *    }
     *  }
     * }
     * */
    @Override
    public List<User> searchPrefix(String keyField, Object keyWord) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.prefixQuery(keyField, (String) keyWord));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }
    /**
     *GET /hotel-user-test/_search
     * {
     *  "query": {
     *    "wildcard": {
     *      "detail": {
     *        "value": "*g1*"
     *      }
     *    }
     *  }
     * }
     * */
    @Override
    public List<User> searchWildcard(String keyField, Object keyWord) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.wildcardQuery(keyField, (String) keyWord));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }

    /**
     * GET /hotel-user-test/_search
     * {
     *  "query": {
     *   "ids": {
     *     "values": ["14411","14313"]
     *   }
     *  }
     * }
     * */
    @Override
    public List<User> searchIds(String ... ids) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.idsQuery().addIds(ids));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }

    /**
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "fuzzy": {
     *       "detail": "abcefgg2"
     *     }
     *   }
     * }
     * - 搜索关键词长度为 2 不允许存在模糊
     *
     * - 搜索关键词长度为3-5 允许一次模糊
     *
     * - 搜索关键词长度大于5 允许最大2模糊
     * */
    @Override
    public List<User> searchFuzzy(String keyField, Object keyWord) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.fuzzyQuery(keyField, keyWord));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }
    /**
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "bool": {
     *       "must": [
     *         { "fuzzy": {
     *       "detail": "abcefgg2"
     *     }},
     *         {"range": {
     *           "property": {
     *             "gte": 18000,
     *             "lte": 20000
     *           }
     *         }}
     *       ]
     *     }
     *   }
     * }
     *
     * 	**must: 相当于&& 同时成立**
     *	**should: 相当于|| 成立一个就行**
     * 	**must_not: 相当于!  不能满足任何一个**
     * */
    @Override
    public List<User> searchBool(String keyField, Object keyWord,String keyFiledRange,double gte, double lte) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.boolQuery().must(QueryBuilders.fuzzyQuery(keyField, keyWord))
                .must(QueryBuilders.rangeQuery(keyFiledRange).gte(gte).lte(lte)));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }

    /**
     *
     *
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "multi_match": {
     *       "query": "efg11",
     *       "fields": ["detail"]
     *     }
     *   }
     * }
     * */
    @Override
    public List<User> searchMultiMatch(Object keyWord,String ... keyFields) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(keyWord,keyFields));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }
    /**
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "query_string": {
     *       "default_field": "detail",
     *       "query": "abcdefg"
     *     }
     *   }
     * }
     * */
    @Override
    public List<User> searchQueryString(String keyField, Object keyWord) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(QueryBuilders.queryStringQuery((String) keyWord).defaultField(keyField));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }


    /**
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *      "query_string": {
     *       "default_field": "detail",
     *       "query": "abcdefg"
     *     }
     *   },
     *   "highlight": {
     *      "post_tags": ["</span>"],
     *     "pre_tags": ["<span style='color:red'>"],
     *     "fields": {
     *       "*":{}
     *     }
     *   }
     * }
     * */
    @Override
    public List<User> searchHighlight(String keyField, Object keyWord) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.postTags("</span>").preTags("<span style='color:red'>");
        nativeSearchQueryBuilder
                .withHighlightBuilder(highlightBuilder.field(keyField))
                .withQuery(QueryBuilders.queryStringQuery((String) keyWord)).build();
        SearchHits<User> searchHits = elasticsearchRestTemplate.search
                (nativeSearchQueryBuilder.build(), User.class, IndexCoordinates.of("hotel-user-test"));
        for (SearchHit<User> searchHit : searchHits.getSearchHits()) {
            searchHit.getContent().setDetail(String.valueOf(searchHit.getHighlightFields().get("detail")));
        }
       return searchHits.stream().map(SearchHit::getContent).collect(Collectors.toList());
    }

    /**
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "match_all": {}
     *   },
     *   "size": 5,
     *   "from": 0
     * }
     * */
    @Override
    public List<User> searchFormAndSize(Integer pageNum, Integer pageSize) {
        Pageable pageable = PageRequest.of(pageNum, pageSize);
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        nativeSearchQueryBuilder.withQuery(matchAllQueryBuilder).withPageable(pageable);
        return nativeSearchToList(nativeSearchQueryBuilder);
    }
    /**
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "match_all": {}
     *   },
     *  "sort": [
     *     {
     *       "property": {
     *         "order": "desc"
     *       }
     *     }
     *   ]
     * }
     * */
    @Override
    public List<User> searchSort(Object keyWord) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        nativeSearchQueryBuilder.withQuery(matchAllQueryBuilder).withSort(SortBuilders.fieldSort((String) keyWord).order(SortOrder.DESC));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }


   /**
    * GET /hotel-user-test/_search
    * {
    *   "query": {
    *     "match_all": {}
    *   },
    *   "_source": ["username","sex,", "detail"]
    * }
    *
    * 先跳过
    * */
    @Override
    public List<User> searchSource(String ... keyFields) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        nativeSearchQueryBuilder.withQuery(matchAllQueryBuilder);
        return nativeSearchToList(nativeSearchQueryBuilder);
    }

    /**
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "bool": {
     *       "must": [
     *         {"match_all": {
     *
     *         }}
     *       ],
     *       "filter": [
     *         {
     *           "term": {
     *             "detail": "abcdefg"
     *           }
     *         }
     *       ]
     *     }
     *   }
     * }
     * */
    @Override
    public List<User> searchFilterTerm(String keyField, Object keyWord) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        nativeSearchQueryBuilder.withQuery(matchAllQueryBuilder)
                .withFilter(boolQueryBuilder.filter(QueryBuilders.termQuery(keyField, keyWord)));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }


    /**
     *GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "geo_bounding_box":{
     *       "location":{
     *         "bottom_right":"30.1,39.9",
     *         "top_left":"35.1,37.2"
     *       }
     *     }
     *   }
     * }
     * */
    @Override
    public List<User> searchGeoBounding(Double tllat, Double tllon,Double brlat, Double brlon) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder
                .withQuery(QueryBuilders.geoBoundingBoxQuery("location").setCorners(tllat,tllon,brlat,brlon));
        return nativeSearchToList(nativeSearchQueryBuilder);
    }


    /**
     *
     * GET /hotel-user-test/_search
     * {
     *   "query": {
     *     "bool": {
     *       "filter": [
     *         {"geo_distance": {
     *           "distance": "20km",
     *           "location": {
     *             "lat": 30.2,
     *             "lon": 39.8
     *           }
     *         }}
     *       ]
     *     }
     *   }
     * }
     *
     * 补充计算两点距离:
     *    GeoDistance.ARC.calculate(srcLat, srcLon, dstLat, dstLon, DistanceUnit.KILOMETERS);
     *                           出发点纬度  出发点精度 目的地纬度 经度     计量单位
     * */
    @Override
    public List<User> searchGeoDistance(double latitude, double longitude,double distance) {
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        GeoDistanceQueryBuilder distanceQueryBuilder = new GeoDistanceQueryBuilder("location");
        distanceQueryBuilder.point(latitude, longitude);
        distanceQueryBuilder.distance(distance, DistanceUnit.KILOMETERS);
        boolQueryBuilder.filter(distanceQueryBuilder);
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
        //按照远近排序
        GeoDistanceSortBuilder distanceSortBuilder =
                new GeoDistanceSortBuilder("location", latitude, longitude);
        distanceSortBuilder.unit(DistanceUnit.KILOMETERS);
        distanceSortBuilder.order(SortOrder.ASC);
        nativeSearchQueryBuilder.withSort(distanceSortBuilder);
        return nativeSearchToList(nativeSearchQueryBuilder);
    }


}
